package de.tum.se.sudoku.impl;

import java.util.Arrays;
import java.util.Random;

/**
 * A Sudoku grid consisting of 9x9 cells
 * 
 * @author michi
 * 
 */
public class Grid {
	private Cell[][] cells;

	public Grid() {
		cells = new Cell[9][9];
		for (int i = 0; i <= 8; i++) {
			Arrays.fill(cells[i], new VariableCell(0));
		}
		buildSudoku();
	}

	public Grid(Cell[][] cells) {
		this.cells = cells;
	}

	public Grid(int[][] values) {
		Cell[][] newCells = new Cell[9][9];

		for (int i = 0; i <= 8; i++) {
			for (int j = 0; j <= 8; j++) {
				if (values[i][j] >= 1 && values[i][j] <= 9)
					newCells[i][j] = new FixedCell(values[i][j]);
				else
					newCells[i][j] = new VariableCell(0);
			}
		}

		this.cells = newCells;
	}

	public Cell[] getRow(int n) {
		if (n >= 0 && n <= 8)
			return cells[n];
		else
			return null;
	}

	public Cell[] getCol(int n) {
		Cell[] reCells = new Cell[9];
		if (n >= 0 && n <= 8) {
			for (int i = 0; i <= 8; i++) {
				reCells[i] = cells[i][n];
			}
		} else
			Arrays.fill(cells, 0);

		return reCells;
	}

	public Cell getCell(int x, int y) {
		if (x >= 0 && y >= 0 && x <= 8 && y <= 8)
			return cells[x][y];
		else
			return null;
	}

	public Cell[][] getCells() {
		return cells;
	}

	public void setCell(int x, int y, Cell cell) {
		if (x >= 0 && y >= 0 && x <= 8 && y <= 8)
			cells[x][y] = cell;
	}

	public String toString() {
		String out = "";

		for (int i = 0; i <= 7; i++) {
			out += Arrays.toString(cells[i]);
			out += "\n";
		}

		out += Arrays.toString(cells[8]);

		return out;
	}

	/**
	 * Checks if the sudoku contains a valid solution
	 * 
	 * @return
	 */
	public boolean isValid() {
		boolean result = true;

		Cell[][] units = getUnits();

		// check all rows, columns and units
		for (int i = 0; i <= 8; i++) {
			result &= validateRC(getRow(i));
			result &= validateRC(getCol(i));
			result &= validateRC(units[i]);
		}

		return result;
	}

	/**
	 * Checks whether a single Column or Row of the sudoku is valid
	 * 
	 * @param cells
	 * @return
	 */
	private static boolean validateRC(Cell[] cells) {
		int[] template = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
		int[] values = new int[9];

		for (int i = 0; i <= 8; i++) {
			if (cells[i].isValid())
				values[i] = cells[i].getValue();
		}

		Arrays.sort(values);
		return Arrays.equals(template, values);
	}

	/**
	 * generates the units
	 * 
	 * @param cells
	 * @return
	 */
	public Cell[][] getUnits() {

		// Hardcoded, bis mir was besseres einfällt, vier Schleifen sind keine
		// Lösung
		Cell[][] units = new Cell[9][9];

		units[0][0] = cells[0][0];
		units[0][1] = cells[0][1];
		units[0][2] = cells[0][2];
		units[0][3] = cells[1][0];
		units[0][4] = cells[1][1];
		units[0][5] = cells[1][2];
		units[0][6] = cells[2][0];
		units[0][7] = cells[2][1];
		units[0][8] = cells[2][2];

		units[1][0] = cells[0][3];
		units[1][1] = cells[0][4];
		units[1][2] = cells[0][5];
		units[1][3] = cells[1][3];
		units[1][4] = cells[1][4];
		units[1][5] = cells[1][5];
		units[1][6] = cells[2][3];
		units[1][7] = cells[2][4];
		units[1][8] = cells[2][5];

		units[2][0] = cells[0][6];
		units[2][1] = cells[0][7];
		units[2][2] = cells[0][8];
		units[2][3] = cells[1][6];
		units[2][4] = cells[1][7];
		units[2][5] = cells[1][8];
		units[2][6] = cells[2][6];
		units[2][7] = cells[2][7];
		units[2][8] = cells[2][8];

		units[3][0] = cells[3][0];
		units[3][1] = cells[3][1];
		units[3][2] = cells[3][2];
		units[3][3] = cells[4][0];
		units[3][4] = cells[4][1];
		units[3][5] = cells[4][2];
		units[3][6] = cells[5][0];
		units[3][7] = cells[5][1];
		units[3][8] = cells[5][2];

		units[4][0] = cells[3][3];
		units[4][1] = cells[3][4];
		units[4][2] = cells[3][5];
		units[4][3] = cells[4][3];
		units[4][4] = cells[4][4];
		units[4][5] = cells[4][5];
		units[4][6] = cells[5][3];
		units[4][7] = cells[5][4];
		units[4][8] = cells[5][5];

		units[5][0] = cells[3][6];
		units[5][1] = cells[3][7];
		units[5][2] = cells[3][8];
		units[5][3] = cells[4][6];
		units[5][4] = cells[4][7];
		units[5][5] = cells[4][8];
		units[5][6] = cells[5][6];
		units[5][7] = cells[5][7];
		units[5][8] = cells[5][8];

		units[6][0] = cells[6][0];
		units[6][1] = cells[6][1];
		units[6][2] = cells[6][2];
		units[6][3] = cells[7][0];
		units[6][4] = cells[7][1];
		units[6][5] = cells[7][2];
		units[6][6] = cells[8][0];
		units[6][7] = cells[8][1];
		units[6][8] = cells[8][2];

		units[7][0] = cells[6][3];
		units[7][1] = cells[6][4];
		units[7][2] = cells[6][5];
		units[7][3] = cells[7][3];
		units[7][4] = cells[7][4];
		units[7][5] = cells[7][5];
		units[7][6] = cells[8][3];
		units[7][7] = cells[8][4];
		units[7][8] = cells[8][5];

		units[8][0] = cells[6][6];
		units[8][1] = cells[6][7];
		units[8][2] = cells[6][8];
		units[8][3] = cells[7][6];
		units[8][4] = cells[7][7];
		units[8][5] = cells[7][8];
		units[8][6] = cells[8][6];
		units[8][7] = cells[8][7];
		units[8][8] = cells[8][8];

		return units;
	}

	public Grid buildSudoku() {
		Random random = new Random();
		int sudoku = random.nextInt(8);
		switch (sudoku) {
		case 0: // Mittel
			setCell(0, 2, new FixedCell(8));
			setCell(0, 3, new FixedCell(3));
			setCell(0, 5, new FixedCell(7));
			setCell(0, 6, new FixedCell(1));
			setCell(1, 1, new FixedCell(6));
			setCell(1, 5, new FixedCell(8));
			setCell(1, 8, new FixedCell(9));
			setCell(2, 0, new FixedCell(3));
			setCell(2, 8, new FixedCell(2));
			setCell(3, 0, new FixedCell(1));
			setCell(3, 2, new FixedCell(4));
			setCell(3, 3, new FixedCell(8));
			setCell(3, 7, new FixedCell(9));
			setCell(4, 0, new FixedCell(7));
			setCell(4, 2, new FixedCell(5));
			setCell(4, 6, new FixedCell(6));
			setCell(4, 8, new FixedCell(1));
			setCell(5, 1, new FixedCell(3));
			setCell(5, 5, new FixedCell(4));
			setCell(5, 6, new FixedCell(2));
			setCell(5, 8, new FixedCell(7));
			setCell(6, 0, new FixedCell(4));
			setCell(6, 8, new FixedCell(3));
			setCell(7, 0, new FixedCell(8));
			setCell(7, 3, new FixedCell(7));
			setCell(7, 7, new FixedCell(2));
			setCell(8, 2, new FixedCell(3));
			setCell(8, 3, new FixedCell(5));
			setCell(8, 5, new FixedCell(2));
			setCell(8, 6, new FixedCell(4));
			break;
		case 1: // Mittel
			setCell(0, 0, new FixedCell(3));
			setCell(0, 4, new FixedCell(1));
			setCell(0, 6, new FixedCell(6));
			setCell(1, 5, new FixedCell(6));
			setCell(1, 8, new FixedCell(2));
			setCell(2, 2, new FixedCell(8));
			setCell(2, 5, new FixedCell(4));
			setCell(2, 6, new FixedCell(1));
			setCell(2, 8, new FixedCell(3));
			setCell(3, 0, new FixedCell(9));
			setCell(3, 4, new FixedCell(8));
			setCell(3, 5, new FixedCell(5));
			setCell(3, 6, new FixedCell(3));
			setCell(4, 0, new FixedCell(4));
			setCell(4, 8, new FixedCell(9));
			setCell(5, 2, new FixedCell(5));
			setCell(5, 3, new FixedCell(1));
			setCell(5, 4, new FixedCell(2));
			setCell(5, 8, new FixedCell(4));
			setCell(6, 0, new FixedCell(2));
			setCell(6, 2, new FixedCell(1));
			setCell(6, 3, new FixedCell(6));
			setCell(6, 6, new FixedCell(4));
			setCell(7, 0, new FixedCell(7));
			setCell(7, 3, new FixedCell(9));
			setCell(8, 2, new FixedCell(6));
			setCell(8, 4, new FixedCell(5));
			setCell(8, 8, new FixedCell(7));
			break;
		case 2: // MIttel
			setCell(0, 0, new FixedCell(4));
			setCell(0, 6, new FixedCell(1));
			setCell(1, 3, new FixedCell(9));
			setCell(1, 4, new FixedCell(1));
			setCell(1, 5, new FixedCell(6));
			setCell(2, 4, new FixedCell(3));
			setCell(2, 6, new FixedCell(9));
			setCell(2, 7, new FixedCell(7));
			setCell(3, 2, new FixedCell(5));
			setCell(3, 4, new FixedCell(7));
			setCell(3, 5, new FixedCell(1));
			setCell(3, 8, new FixedCell(9));
			setCell(4, 0, new FixedCell(7));
			setCell(4, 1, new FixedCell(9));
			setCell(4, 4, new FixedCell(8));
			setCell(4, 7, new FixedCell(5));
			setCell(4, 8, new FixedCell(2));
			setCell(5, 0, new FixedCell(3));
			setCell(5, 3, new FixedCell(4));
			setCell(5, 4, new FixedCell(5));
			setCell(5, 6, new FixedCell(6));
			setCell(6, 1, new FixedCell(7));
			setCell(6, 2, new FixedCell(4));
			setCell(6, 4, new FixedCell(6));
			setCell(7, 3, new FixedCell(8));
			setCell(7, 4, new FixedCell(4));
			setCell(7, 5, new FixedCell(5));
			setCell(8, 2, new FixedCell(8));
			setCell(8, 8, new FixedCell(5));
			;
			break;
		case 3: // Leicht
			setCell(0, 5, new FixedCell(3));
			setCell(0, 6, new FixedCell(7));
			setCell(0, 8, new FixedCell(9));
			setCell(1, 0, new FixedCell(6));
			setCell(1, 7, new FixedCell(4));
			setCell(2, 0, new FixedCell(4));
			setCell(2, 2, new FixedCell(3));
			setCell(2, 3, new FixedCell(2));
			setCell(2, 6, new FixedCell(1));
			setCell(3, 1, new FixedCell(7));
			setCell(3, 2, new FixedCell(8));
			setCell(3, 4, new FixedCell(9));
			setCell(3, 5, new FixedCell(6));
			setCell(3, 7, new FixedCell(3));
			setCell(3, 8, new FixedCell(4));
			setCell(4, 2, new FixedCell(6));
			setCell(4, 3, new FixedCell(3));
			setCell(4, 4, new FixedCell(2));
			setCell(4, 5, new FixedCell(1));
			setCell(4, 6, new FixedCell(5));
			setCell(5, 0, new FixedCell(3));
			setCell(5, 1, new FixedCell(5));
			setCell(5, 3, new FixedCell(8));
			setCell(5, 4, new FixedCell(4));
			setCell(5, 6, new FixedCell(6));
			setCell(5, 7, new FixedCell(9));
			setCell(6, 2, new FixedCell(5));
			setCell(6, 5, new FixedCell(4));
			setCell(6, 6, new FixedCell(3));
			setCell(6, 8, new FixedCell(6));
			setCell(7, 1, new FixedCell(3));
			setCell(7, 8, new FixedCell(8));
			setCell(8, 0, new FixedCell(8));
			setCell(8, 2, new FixedCell(4));
			setCell(8, 3, new FixedCell(7));
			;
			break;
		case 4: // Leicht
			setCell(0, 0, new FixedCell(7));
			setCell(0, 4, new FixedCell(2));
			setCell(0, 7, new FixedCell(9));
			setCell(1, 2, new FixedCell(2));
			setCell(1, 3, new FixedCell(5));
			setCell(1, 5, new FixedCell(7));
			setCell(1, 6, new FixedCell(8));
			setCell(1, 7, new FixedCell(1));
			setCell(1, 8, new FixedCell(6));
			setCell(2, 0, new FixedCell(4));
			setCell(2, 6, new FixedCell(2));
			setCell(2, 7, new FixedCell(5));
			setCell(3, 3, new FixedCell(2));
			setCell(3, 4, new FixedCell(8));
			setCell(3, 5, new FixedCell(4));
			setCell(3, 7, new FixedCell(6));
			setCell(4, 0, new FixedCell(8));
			setCell(4, 4, new FixedCell(3));
			setCell(4, 8, new FixedCell(4));
			setCell(5, 1, new FixedCell(4));
			setCell(5, 3, new FixedCell(6));
			setCell(5, 4, new FixedCell(1));
			setCell(5, 5, new FixedCell(9));
			setCell(6, 1, new FixedCell(1));
			setCell(6, 2, new FixedCell(8));
			setCell(6, 8, new FixedCell(9));
			setCell(7, 0, new FixedCell(9));
			setCell(7, 1, new FixedCell(3));
			setCell(7, 2, new FixedCell(7));
			setCell(7, 3, new FixedCell(4));
			setCell(7, 5, new FixedCell(1));
			setCell(7, 6, new FixedCell(5));
			setCell(8, 1, new FixedCell(2));
			setCell(8, 4, new FixedCell(7));
			setCell(8, 8, new FixedCell(1));
			;
			break;
		case 5: // Leicht
			setCell(0, 0, new FixedCell(6));
			setCell(0, 1, new FixedCell(2));
			setCell(0, 6, new FixedCell(4));
			setCell(0, 8, new FixedCell(8));
			setCell(1, 4, new FixedCell(7));
			setCell(1, 7, new FixedCell(5));
			setCell(2, 0, new FixedCell(1));
			setCell(2, 1, new FixedCell(5));
			setCell(2, 2, new FixedCell(3));
			setCell(2, 5, new FixedCell(6));
			setCell(2, 6, new FixedCell(2));
			setCell(2, 7, new FixedCell(9));
			setCell(3, 5, new FixedCell(9));
			setCell(3, 7, new FixedCell(4));
			setCell(3, 8, new FixedCell(2));
			setCell(4, 2, new FixedCell(4));
			setCell(4, 3, new FixedCell(8));
			setCell(4, 4, new FixedCell(2));
			setCell(4, 5, new FixedCell(7));
			setCell(4, 6, new FixedCell(5));
			setCell(5, 0, new FixedCell(2));
			setCell(5, 1, new FixedCell(6));
			setCell(5, 4, new FixedCell(5));
			setCell(6, 1, new FixedCell(1));
			setCell(6, 2, new FixedCell(2));
			setCell(6, 3, new FixedCell(5));
			setCell(6, 6, new FixedCell(9));
			setCell(6, 7, new FixedCell(3));
			setCell(6, 8, new FixedCell(6));
			setCell(7, 1, new FixedCell(9));
			setCell(7, 4, new FixedCell(1));
			setCell(8, 0, new FixedCell(3));
			setCell(8, 2, new FixedCell(8));
			setCell(8, 7, new FixedCell(7));
			setCell(8, 8, new FixedCell(5));
			;
			break;
		case 6: // Schwer
			setCell(0, 2, new FixedCell(4));
			setCell(0, 3, new FixedCell(3));
			setCell(0, 4, new FixedCell(8));
			setCell(1, 1, new FixedCell(2));
			setCell(1, 2, new FixedCell(3));
			setCell(1, 5, new FixedCell(9));
			setCell(1, 6, new FixedCell(8));
			setCell(2, 1, new FixedCell(6));
			setCell(2, 5, new FixedCell(2));
			setCell(2, 6, new FixedCell(5));
			setCell(3, 0, new FixedCell(3));
			setCell(3, 8, new FixedCell(8));
			setCell(4, 1, new FixedCell(4));
			setCell(4, 2, new FixedCell(2));
			setCell(4, 6, new FixedCell(3));
			setCell(4, 7, new FixedCell(6));
			setCell(5, 0, new FixedCell(6));
			setCell(5, 7, new FixedCell(2));
			setCell(6, 2, new FixedCell(5));
			setCell(6, 3, new FixedCell(4));
			setCell(6, 7, new FixedCell(8));
			setCell(7, 2, new FixedCell(8));
			setCell(7, 3, new FixedCell(1));
			setCell(7, 6, new FixedCell(2));
			setCell(7, 7, new FixedCell(3));
			setCell(8, 4, new FixedCell(7));
			setCell(8, 5, new FixedCell(3));
			setCell(8, 6, new FixedCell(4));
		case 7: // Schwer
			setCell(0, 0, new FixedCell(8));
			setCell(0, 1, new FixedCell(3));
			setCell(0, 5, new FixedCell(9));
			setCell(1, 2, new FixedCell(7));
			setCell(1, 3, new FixedCell(2));
			setCell(1, 4, new FixedCell(1));
			setCell(1, 5, new FixedCell(8));
			setCell(1, 8, new FixedCell(9));
			setCell(2, 4, new FixedCell(7));
			setCell(2, 8, new FixedCell(1));
			setCell(3, 5, new FixedCell(7));
			setCell(3, 7, new FixedCell(6));
			setCell(4, 0, new FixedCell(4));
			setCell(4, 2, new FixedCell(8));
			setCell(4, 6, new FixedCell(9));
			setCell(4, 8, new FixedCell(3));
			setCell(5, 1, new FixedCell(1));
			setCell(5, 3, new FixedCell(9));
			setCell(6, 0, new FixedCell(3));
			setCell(6, 4, new FixedCell(2));
			setCell(7, 0, new FixedCell(2));
			setCell(7, 3, new FixedCell(8));
			setCell(7, 4, new FixedCell(5));
			setCell(7, 5, new FixedCell(1));
			setCell(7, 6, new FixedCell(3));
			setCell(8, 3, new FixedCell(3));
			setCell(8, 7, new FixedCell(4));
			setCell(8, 8, new FixedCell(2));
			;
			break;
		case 8: // Schwer
			setCell(0, 3, new FixedCell(9));
			setCell(0, 7, new FixedCell(4));
			setCell(1, 0, new FixedCell(9));
			setCell(1, 3, new FixedCell(4));
			setCell(1, 6, new FixedCell(3));
			setCell(2, 0, new FixedCell(1));
			setCell(2, 3, new FixedCell(6));
			setCell(2, 8, new FixedCell(7));
			setCell(3, 1, new FixedCell(2));
			setCell(3, 5, new FixedCell(7));
			setCell(3, 7, new FixedCell(8));
			setCell(4, 0, new FixedCell(5));
			setCell(4, 1, new FixedCell(8));
			setCell(4, 4, new FixedCell(6));
			setCell(4, 7, new FixedCell(3));
			setCell(4, 8, new FixedCell(4));
			setCell(5, 1, new FixedCell(3));
			setCell(5, 3, new FixedCell(5));
			setCell(5, 7, new FixedCell(1));
			setCell(6, 0, new FixedCell(2));
			setCell(6, 5, new FixedCell(6));
			setCell(6, 8, new FixedCell(5));
			setCell(7, 2, new FixedCell(8));
			setCell(7, 5, new FixedCell(4));
			setCell(7, 8, new FixedCell(6));
			setCell(8, 1, new FixedCell(7));
			setCell(8, 5, new FixedCell(2));
			;
			break;
		}
		return this;
	}

	public void printGrid() {
		System.out.println("-----------------------");
		for (int i = 0; i < 9; ++i) {
			String rowString = "";
			for (int j = 0; j < 9; ++j) {
				rowString += "[" + getCell(i, j) + "]";
			}
			System.out.println(rowString);
		}
		System.out.println("-----------------------");
	}
}
